package org.bigk.invoices.services;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.bigk.invoices.exceptions.ServiceException;
import org.bigk.invoices.model.PaymentKind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service("paymentKindsService")
@Transactional
public class PaymentKindsServiceImpl implements PaymentKindsService {

	private static final Logger logger = LoggerFactory.getLogger(PaymentKindsServiceImpl.class);

	private static final String ALL_PAYMENT_KINDS_QUERY =
			"SELECT pk FROM PaymentKind pk ORDER BY pk.id ASC";
	
	@PersistenceContext
	protected EntityManager em;

	@Override
	public List<PaymentKind> listAllItems() throws ServiceException {
		logger.debug("listAllItems() - start");

		TypedQuery<PaymentKind> query = em.createQuery(ALL_PAYMENT_KINDS_QUERY, PaymentKind.class);
		List<PaymentKind> list = query.getResultList();

		logger.debug("listAllItems() - end - read [{}] element(s)", CollectionUtils.size(list));
		return list;
	}

	@Override
	public PaymentKind getPaymentKind(Long id) throws ServiceException {
		logger.debug("getPaymentKind(id=[{}]) - start", id);
		
		PaymentKind paymentKind = em.find(PaymentKind.class, id);

		logger.debug("getPaymentKind() - end - return value=[{}]", paymentKind);
		return paymentKind;
	}

	public Date calculatePaymentDate(Date soldDate, Long paymentKindId)
			throws ServiceException {
		logger.debug("calculatePaymentDate(soldDate=[{}], paymentKindId=[{}]) - start", soldDate, paymentKindId);

		PaymentKind paymentKind = this.getPaymentKind(paymentKindId);
		if (paymentKind == null) {
			throw new ServiceException("No PaymentKind read for id: " + paymentKindId);
		}
		Date returnDate = calculatePaymentDate(soldDate, paymentKind.getValue());

		logger.debug("calculatePaymentDate() - end - return value=[{}]", returnDate);
		return returnDate;
	}

	public Date calculatePaymentDate(Date soldDate, String paymentKindValue)
			throws ServiceException {
		
		logger.debug("calculatePaymentDate(soldDate=[{}], paymentKindValue=[{}]) - start", soldDate, paymentKindValue);
		
		Calendar cal = Calendar.getInstance();
		cal.setTime(soldDate);
		
		String formule = paymentKindValue;
		if (StringUtils.isNotBlank(formule)) {
			formule = StringUtils.lowerCase(formule);
			
			// detect 'period': one of 'd' (day) or 'm' (month)
			String period = null;
			if (formule.startsWith("d")) {
				period = "d";
				formule = formule.substring(1);
			} else if (formule.startsWith("m")) {
				period = "m";
				formule = formule.substring(1);
			}
			
			// detect 'sign': one of '+' or '-'
			int sign = 1;
			if (formule.startsWith("+"))
				formule = formule.substring(1);
			else if (formule.startsWith("-")) {
				sign = -1;
				formule = formule.substring(1);
			}

			logger.debug("calculatePaymentDate() - formule=[{}], period=[{}], sign=[{}]",
					new Object[] {formule, period, sign});
			
			if ("d".equals(period)) {
				int days = sign * NumberUtils.toInt(formule, 0);
				cal.add(5, days);
			} else if ("m".equals(period)) {
				int months = sign * NumberUtils.toInt(formule, 0);
				cal.add(2, months);
			}
		}
		
		Date returnDate = cal.getTime();

		logger.debug("calculatePaymentDate() - end - return value=[{}]", returnDate);
		return returnDate;
	}
}
